#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

/**
 * 简化版代码量统计工具
 * 支持生成HTML报告
 */

// 配置项
const CONFIG = {
  extensions: {
    'TypeScript': ['.ts', '.tsx'],
    'JavaScript': ['.js', '.jsx'],
    'CSS/LESS/SCSS': ['.css', '.less', '.scss', '.sass'],
    'HTML': ['.html', '.htm'],
    'JSON': ['.json'],
    'Markdown': ['.md', '.markdown'],
    'Config': ['.yml', '.yaml', '.toml', '.ini'],
    'Other': ['.txt', '.xml']
  },
  
  ignoreDirs: [
    'node_modules', '.git', 'dist', 'build', 'coverage',
    '.next', '.nuxt', 'out', 'public', 'static',
    'assets/images', 'assets/icons'
  ],
  
  ignoreFiles: [
    '.gitignore', '.eslintrc', '.prettierrc',
    'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'
  ]
};

class SimpleCodeStats {
  constructor() {
    this.stats = {
      totalFiles: 0,
      totalLines: 0,
      totalCodeLines: 0,
      totalBlankLines: 0,
      totalCommentLines: 0,
      fileTypes: {},
      largestFiles: []
    };
    
    Object.keys(CONFIG.extensions).forEach(type => {
      this.stats.fileTypes[type] = {
        files: 0, lines: 0, codeLines: 0, blankLines: 0, commentLines: 0
      };
    });
  }

  shouldIgnoreDir(dirName) {
    return CONFIG.ignoreDirs.some(ignore => 
      dirName === ignore || dirName.startsWith(ignore)
    );
  }

  shouldIgnoreFile(fileName) {
    return CONFIG.ignoreFiles.some(ignore => 
      fileName === ignore || fileName.endsWith(ignore)
    );
  }

  getFileType(filePath) {
    const ext = path.extname(filePath).toLowerCase();
    for (const [type, extensions] of Object.entries(CONFIG.extensions)) {
      if (extensions.includes(ext)) return type;
    }
    return null;
  }

  analyzeFile(filePath) {
    try {
      const content = fs.readFileSync(filePath, 'utf-8');
      const lines = content.split('\n');
      
      let codeLines = 0, blankLines = 0, commentLines = 0;
      const ext = path.extname(filePath).toLowerCase();
      const isJsTs = ['.js', '.jsx', '.ts', '.tsx'].includes(ext);
      const isCss = ['.css', '.less', '.scss', '.sass'].includes(ext);
      
      let inBlockComment = false;
      
      for (let line of lines) {
        const trimmedLine = line.trim();
        
        if (trimmedLine === '') {
          blankLines++;
          continue;
        }
        
        if (isJsTs) {
          if (trimmedLine.includes('/*')) inBlockComment = true;
          if (trimmedLine.includes('*/')) {
            inBlockComment = false;
            commentLines++;
            continue;
          }
          if (inBlockComment || trimmedLine.startsWith('//')) {
            commentLines++;
            continue;
          }
        }
        
        if (isCss) {
          if (trimmedLine.includes('/*')) inBlockComment = true;
          if (trimmedLine.includes('*/')) {
            inBlockComment = false;
            commentLines++;
            continue;
          }
          if (inBlockComment) {
            commentLines++;
            continue;
          }
        }
        
        if ((ext === '.html' || ext === '.htm') && 
            (trimmedLine.startsWith('<!--') || trimmedLine.includes('<!--'))) {
          commentLines++;
          continue;
        }
        
        codeLines++;
      }
      
      return { totalLines: lines.length, codeLines, blankLines, commentLines };
    } catch (error) {
      return { totalLines: 0, codeLines: 0, blankLines: 0, commentLines: 0 };
    }
  }

  scanDirectory(dirPath, relativePath = '') {
    try {
      const items = fs.readdirSync(dirPath);
      
      for (const item of items) {
        const fullPath = path.join(dirPath, item);
        const itemRelativePath = path.join(relativePath, item);
        
        let stat;
        try {
          stat = fs.statSync(fullPath);
        } catch (error) {
          continue;
        }
        
        if (stat.isDirectory()) {
          if (this.shouldIgnoreDir(item)) continue;
          this.scanDirectory(fullPath, itemRelativePath);
        } else if (stat.isFile()) {
          if (this.shouldIgnoreFile(item)) continue;
          if (stat.size > 10 * 1024 * 1024) continue;
          
          const fileType = this.getFileType(fullPath);
          if (!fileType) continue;
          
          const analysis = this.analyzeFile(fullPath);
          
          // 更新总体统计
          this.stats.totalFiles++;
          this.stats.totalLines += analysis.totalLines;
          this.stats.totalCodeLines += analysis.codeLines;
          this.stats.totalBlankLines += analysis.blankLines;
          this.stats.totalCommentLines += analysis.commentLines;
          
          // 更新文件类型统计
          const typeStats = this.stats.fileTypes[fileType];
          typeStats.files++;
          typeStats.lines += analysis.totalLines;
          typeStats.codeLines += analysis.codeLines;
          typeStats.blankLines += analysis.blankLines;
          typeStats.commentLines += analysis.commentLines;
          
          // 记录大文件
          if (analysis.totalLines > 200) {
            this.stats.largestFiles.push({
              path: itemRelativePath,
              lines: analysis.totalLines,
              codeLines: analysis.codeLines,
              type: fileType
            });
          }
          
          if (this.stats.totalFiles % 25 === 0) {
            process.stdout.write(`\r已扫描 ${this.stats.totalFiles} 个文件...`);
          }
        }
      }
    } catch (error) {
      console.warn(`\n无法扫描目录: ${dirPath}`, error.message);
    }
  }

  generateHTMLReport() {
    try {
      const commentRatio = ((this.stats.totalCommentLines / this.stats.totalCodeLines) * 100).toFixed(1);
      
      // 生成文件类型表格行
      const typeRows = Object.entries(this.stats.fileTypes)
        .filter(([_, stats]) => stats.files > 0)
        .sort(([, a], [, b]) => b.codeLines - a.codeLines)
        .map(([type, stats]) => {
          const percentage = ((stats.codeLines / this.stats.totalCodeLines) * 100).toFixed(1);
          return `<tr>
            <td>${type}</td>
            <td>${stats.files.toLocaleString()}</td>
            <td>${stats.lines.toLocaleString()}</td>
            <td>${stats.codeLines.toLocaleString()}</td>
            <td>${percentage}%</td>
          </tr>`;
        }).join('');

      // 生成大文件列表
      const largeFilesList = this.stats.largestFiles
        .sort((a, b) => b.lines - a.lines)
        .slice(0, 10)
        .map(file => `<div class="file-item">
          <strong>${file.path}</strong> 
          <span style="color: #666;">(${file.type})</span>
          <span style="float: right; color: #007bff;">${file.lines} 行 / ${file.codeLines} 代码行</span>
        </div>`).join('');

      const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>代码量统计报告</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
        .container { max-width: 1200px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        h1 { color: #333; text-align: center; margin-bottom: 30px; }
        .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px; }
        .stat-card { background: #f8f9fa; padding: 20px; border-radius: 6px; text-align: center; }
        .stat-number { font-size: 2em; font-weight: bold; color: #007bff; }
        .stat-label { color: #666; margin-top: 5px; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
        th { background: #f8f9fa; font-weight: 600; }
        .large-files { margin-top: 30px; }
        .file-item { padding: 10px; margin: 5px 0; background: #f8f9fa; border-radius: 4px; }
    </style>
</head>
<body>
    <div class="container">
        <h1>📊 代码量统计报告</h1>
        
        <div class="stats-grid">
            <div class="stat-card">
                <div class="stat-number">${this.stats.totalFiles.toLocaleString()}</div>
                <div class="stat-label">总文件数</div>
            </div>
            <div class="stat-card">
                <div class="stat-number">${this.stats.totalLines.toLocaleString()}</div>
                <div class="stat-label">总行数</div>
            </div>
            <div class="stat-card">
                <div class="stat-number">${this.stats.totalCodeLines.toLocaleString()}</div>
                <div class="stat-label">代码行数</div>
            </div>
            <div class="stat-card">
                <div class="stat-number">${commentRatio}%</div>
                <div class="stat-label">注释率</div>
            </div>
        </div>

        <h2>📋 文件类型统计</h2>
        <table>
            <thead>
                <tr><th>类型</th><th>文件数</th><th>总行数</th><th>代码行</th><th>占比</th></tr>
            </thead>
            <tbody>
                ${typeRows}
            </tbody>
        </table>

        <div class="large-files">
            <h2>📄 大文件列表 (>200行)</h2>
            ${largeFilesList}
        </div>

        <p style="text-align: center; color: #666; margin-top: 40px;">
            报告生成时间: ${new Date().toLocaleString('zh-CN')}
        </p>
    </div>
</body>
</html>`;

      fs.writeFileSync('code-stats-report.html', html);
      console.log('\n📄 HTML报告已生成: code-stats-report.html');
    } catch (error) {
      console.error('生成HTML报告时出错:', error.message);
    }
  }

  generateConsoleReport() {
    console.log('\n📊 代码量统计报告');
    console.log('='.repeat(60));
    
    console.log('\n📈 总体统计:');
    console.log(`  总文件数: ${this.stats.totalFiles.toLocaleString()}`);
    console.log(`  总行数: ${this.stats.totalLines.toLocaleString()}`);
    console.log(`  代码行数: ${this.stats.totalCodeLines.toLocaleString()}`);
    console.log(`  空白行数: ${this.stats.totalBlankLines.toLocaleString()}`);
    console.log(`  注释行数: ${this.stats.totalCommentLines.toLocaleString()}`);
    
    console.log('\n📋 按文件类型统计:');
    console.log('-'.repeat(80));
    console.log('类型'.padEnd(15) + '文件数'.padEnd(10) + '总行数'.padEnd(12) + '代码行'.padEnd(12) + '占比');
    console.log('-'.repeat(80));
    
    const sortedTypes = Object.entries(this.stats.fileTypes)
      .filter(([_, stats]) => stats.files > 0)
      .sort(([, a], [, b]) => b.codeLines - a.codeLines);
    
    for (const [type, stats] of sortedTypes) {
      const percentage = ((stats.codeLines / this.stats.totalCodeLines) * 100).toFixed(1);
      console.log(
        type.padEnd(15) +
        stats.files.toLocaleString().padEnd(10) +
        stats.lines.toLocaleString().padEnd(12) +
        stats.codeLines.toLocaleString().padEnd(12) +
        `${percentage}%`
      );
    }
    
    console.log('\n✅ 统计完成!');
  }

  run(targetPath = '.', generateHTML = false) {
    console.log('🚀 开始统计代码量...');
    console.log(`📁 扫描目录: ${path.resolve(targetPath)}`);
    
    const startTime = Date.now();
    this.scanDirectory(targetPath);
    const endTime = Date.now();
    
    console.log(`\n⏱️  扫描耗时: ${((endTime - startTime) / 1000).toFixed(2)}秒`);
    
    this.generateConsoleReport();
    
    if (generateHTML) {
      this.generateHTMLReport();
    }
  }
}

// 主程序
if (require.main === module) {
  const args = process.argv.slice(2);
  const targetPath = args[0] || '.';
  const generateHTML = args.includes('--html') || args.includes('-h');
  
  const codeStats = new SimpleCodeStats();
  codeStats.run(targetPath, generateHTML);
}

module.exports = SimpleCodeStats;
